#pragma once
#include <atomic>
#include <functional>
#include <iostream>
#include <list>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_map>
extern "C" {
#include "json-c/json.h"
#include "libubox/blobmsg_json.h"
#include "libubox/uloop.h"
#include "libubox/ustream.h"
#include "libubox/utils.h"
#include "libubus.h"
}
#include "IMessageReceiver.h"

using MsgType = const std::string &;
using MessageReceiver = void(MsgType message);

struct UBusCallback
{
    std::function<MessageReceiver> callback;
};

class UBusCallbackStaticManager
{
   public:
    UBusCallbackStaticManager();
    virtual ~UBusCallbackStaticManager() = default;

    static void addCallbackHandler(const std::string &key, IMessageReceiver *handler)
    {
        GetEventsMap().emplace(key, handler);
    }
    static bool removeCallbackHandler(const std::string &key, IMessageReceiver *handler)
    {
        auto ps = GetEventsMap().equal_range(key);
        for (auto it = ps.first; it != ps.second; it++)
        {
            if (it->second == handler)
            {
                GetEventsMap().erase(it++);
                return false;
            }
        }
        return false;
    }

    static std::unordered_multimap<std::string, IMessageReceiver *> &GetEventsMap()
    {
        static std::unordered_multimap<std::string, IMessageReceiver *> events;
        return events;
    }
};

class UbusEvent
{
   private:
    const char *sock_path = nullptr;
    struct ubus_context *ctx = nullptr;
    std::mutex m_mutex;

    std::thread uloopThread;
    std::atomic<bool> threadRunning;

    int ubus_init(const char *path);
    int ubus_done();
    int ubus_register_events(const char *key);

   protected:
    void StartUloopThread();
    void StopUloopThread();

   public:
    UbusEvent();
    virtual ~UbusEvent();

    int SendMsg(const std::string &key, const std::string &json_msg);

    void Subscribe(const std::string &key, IMessageReceiver *callback);
    void unSubscribe(const std::string &key, IMessageReceiver *callback);
};
